//
//  ast.cpp
//  ltlbmc
//
//  Created by GD Huang on 5/18/12.
//  Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//
#include <iostream>
#include <vector>
#include <ext/hash_map>
#include "ast.h"



extern "C" {
#include <caml/mlvalues.h>
#include <assert.h>

#include "cil_type.h"
#include "cil_stub.h"
#include "cil_wrap.h"    
    
}


cilast::varinfo* copyVarinfo(CilVarinfo cilVarinfo);
cilast::typ* copyTyp(CilTyp cilTyp);
cilast::lval* copyLval(CilLval cilLval);
cilast::exp* copyExp(CilExp cilExp);
cilast::stmtkind* copyStmtkind(CilStmtkind cilStmtkind);
cilast::block* copyBlock(CilBlock cilBlock);
cilast::offset* copyOffset(CilOffset cilOffset);
cilast::init* copyInit(CilInit cilInit);
cilast::fieldinfo* copyFieldinfo(CilFieldinfo cilFieldinfo);
cilast::fkind copyFkind(CilFkind cilFkind);
cilast::enuminfo* copyEnuminfo(CilEnuminfo cilEnuminfo);
cilast::attribute* copyAttribute(CilAttribute cilAttribute);
cilast::ikind copyIkind(CilIkind cilIkind);
cilast::compinfo* copyCompinfo(CilCompinfo cilCompinfo);
cilast::attrparam* copyAttrparam(CilAttrparam cilAttrparam);
    

__gnu_cxx::hash_map<long, void*> symbolTable;
cilast::fundec* mainFundec;

long get_val(value v) {
    long res = (long)v;
    return res;
    /*
    if(Is_block(v)) {
        return (long)Data_custom_val(v) - sizeof(void*);
    }
    else {
        return Long_val(v);
    }*/
}

template <typename T>
std::vector<T*> copyArray(value list, T* (*functor)(value)) {
    CAMLparam1(list);
    std::vector<T*> array;
    
    for(; list!=NIL; list = LIST_NEXT(list)) {
        array.push_back(functor(LIST_DATA(list)));
    }
    return array;
}



/*
 Instructions
 */
cilast::location* copyLocation(CilLocation cilLocation) {
    CAMLparam1(cilLocation);
    cilast::location* result;
    
    if(symbolTable.find(get_val(cilLocation))!=symbolTable.end()) {
        result = (cilast::location*)symbolTable[get_val(cilLocation)]; 
    }
    else {
        result = new cilast::location();
        symbolTable[get_val(cilLocation)] = result;

        result->line = Int_val(get_line(cilLocation));
        result->file = Copy_string(get_file(cilLocation));
        result->byte = Int_val(get_byte(cilLocation));    
    }
    
    result->sink = cilLocation;
    return result;
    
}

cilast::typsig* copyTypsig(CilTypsig cilTypsig) {
    CAMLparam1(cilTypsig);
    

    switch(Tag_val(cilTypsig)) {
        case TSArray: {
            cilast::TSArray* result = new cilast::TSArray();
            result->mtypsig = copyTypsig(Field(cilTypsig, 0));
            
            if(is_None(Field(cilTypsig, 1)))
                result->mint = NULL;
            else
                result->mint = Int64_val(Some_val(Field(cilTypsig, 1)));
            //result->mattributes
            result->sink = cilTypsig;
            return result;
        }
        case TSPtr: {
            cilast::TSPtr* result = new cilast::TSPtr();
            result->mtypsig = copyTypsig(Field(cilTypsig, 0));
            //result->mattributes
            result->sink = cilTypsig;
            return result;
        }
        case TSComp: {
            cilast::TSComp* result = new cilast::TSComp();
            result->mbool = Bool_val(Field(cilTypsig, 0));
            result->mstr = Copy_string(Field(cilTypsig, 1));
            //result->mattributes
            result->sink = cilTypsig;
            return result;
        }
        case TSFun: {
            cilast::TSFun* result = new cilast::TSFun();
            result->mtypsig = copyTypsig(Field(cilTypsig, 0));
            result->mtypsigs = copyArray(Field(cilTypsig, 1), &copyTypsig);
            result->mbool = Bool_val(Field(cilTypsig, 2));
            //result->mattributes
            result->sink = cilTypsig;
            return result;
        }
        case TSEnum: {
            cilast::TSEnum* result = new cilast::TSEnum();
            result->mstr = Copy_string(Field(cilTypsig, 0));
            //result->mattributes
            result->sink = cilTypsig;
            return result;
        }
        case TSBase: {
            cilast::TSBase* result = new cilast::TSBase();
            result->mtyp = copyTyp(Field(cilTypsig, 0));
            result->sink = cilTypsig;
            return result;
        }
        default:
            std::cout<<"copyTypsig()::exit()";
            exit(1);
    }
        

}

cilast::instr* copyInstr(CilInstr cilInstr) {
    CAMLparam1(cilInstr);


    switch(Tag_val(cilInstr)) {
        case Set: {
            cilast::Set* result = new cilast::Set();
            result->mlval = copyLval(Field(cilInstr, 0));
            result->mexp = copyExp(Field(cilInstr, 1));
            result->mlocation = copyLocation(Field(cilInstr, 2));
            result->sink = cilInstr;
            return result;
        }
        case Call: {
            cilast::Call* result = new cilast::Call();
            if(is_None(Field(cilInstr, 0)))
                result->mlval = NULL;
            else
                result->mlval = copyLval(Some_val(Field(cilInstr, 0)));
                        
            result->mexp = copyExp(Field(cilInstr, 1));
            result->mexps = copyArray(Field(cilInstr, 2), &copyExp);
            result->mlocation = copyLocation(Field(cilInstr, 3));
            result->sink = cilInstr;
            return result;
        }
        case Asm: {            
            std::cout<<"copyInstr()::asm is not implemented";
            break;
        }
        default:
            std::cout<<"copyInstr()::exit()";
            exit(1);
    }
    
}

/*
 Statements
 */
cilast::label* copyLabel(CilLabel cilLabel) {
    CAMLparam1(cilLabel);
    

    switch(Tag_val(cilLabel)) {
        case Label: {
            cilast::Label* result = new cilast::Label();
            result->mstr = Copy_string(Field(cilLabel, 0));
            result->mlocation = copyLocation(Field(cilLabel, 1));
            result->mbool = Bool_val(Field(cilLabel, 2));
            result->sink = cilLabel;
            return result;
        }
        case Case: {
            cilast::Case* result = new cilast::Case();
            result->mexp = copyExp(Field(cilLabel, 0));
            result->mlocation = copyLocation(Field(cilLabel, 1));
            result->sink = cilLabel;
            return result;
        }
        case Default: {
            cilast::Default* result = new cilast::Default();
            result->mlocation = copyLocation(Field(cilLabel, 0));
            result->sink = cilLabel;
            return result;
        }
        default:
            std::cout<<"copyLabel()::exit()";
            exit(1);
    }
    

}

cilast::stmt* copyStmt(CilStmt cilStmt) {
    CAMLparam1(cilStmt);
    
    
    if(symbolTable.find(get_val(cilStmt))!=symbolTable.end())
        return (cilast::stmt*)symbolTable[get_val(cilStmt)]; 
    
    cilast::stmt* result = new cilast::stmt();
    symbolTable[get_val(cilStmt)] = result;
    
    result->labels = copyArray(get_labels(cilStmt), &copyLabel);
    result->skind = copyStmtkind(get_skind(cilStmt));
    result->sid = Int_val(get_sid(cilStmt));
    result->succs = copyArray(get_succs(cilStmt), &copyStmt);
    result->preds = copyArray(get_preds(cilStmt), &copyStmt);
    result->sink = cilStmt;
    
    return result;
}

cilast::stmtkind* copyStmtkind(CilStmtkind cilStmtkind) {
    CAMLparam1(cilStmtkind);
    

    switch(Tag_val(cilStmtkind)) {
        case Instr: {
            cilast::Instr* result = new cilast::Instr();
            result->minstrs = copyArray(Field(cilStmtkind, 0), &copyInstr);
            result->sink = cilStmtkind;
            return result;
        }
        case Return: {
            cilast::Return* result = new cilast::Return();
            if(is_None(Field(cilStmtkind, 0)))
                result->mexp = NULL;
            else
                result->mexp = copyExp(Some_val(Field(cilStmtkind, 0)));
            
            result->mlocation = copyLocation(Field(cilStmtkind, 1));
            result->sink = cilStmtkind;
            return result;
        }
        case Goto: {
            cilast::Goto* result = new cilast::Goto();
            result->mstmt = copyStmt(DEREF(Field(cilStmtkind, 0)));
            result->mlocation = copyLocation(Field(cilStmtkind, 1));
            result->sink = cilStmtkind;
            return result;
        }
        case Break: {
            cilast::Break* result = new cilast::Break();
            result->mlocation = copyLocation(Field(cilStmtkind, 0));
            result->sink = cilStmtkind;
            return result;
        }
        case Continue: {
            cilast::Continue* result = new cilast::Continue();
            result->mlocation = copyLocation(Field(cilStmtkind, 0));
            result->sink = cilStmtkind;
            return result;
        }
        case If: {
            cilast::If* result = new cilast::If();
            result->mexp = copyExp(Field(cilStmtkind, 0));
            result->mthenBlock = copyBlock(Field(cilStmtkind, 1));
            result->melseBlock = copyBlock(Field(cilStmtkind, 2));
            result->mlocation = copyLocation(Field(cilStmtkind, 3));
            result->sink = cilStmtkind;
            return result;
        }
        case Switch: {
            cilast::Switch* result = new cilast::Switch();
            result->mexp = copyExp(Field(cilStmtkind, 0));
            result->mblock = copyBlock(Field(cilStmtkind, 1));
            result->mstmts = copyArray(Field(cilStmtkind, 2), &copyStmt);
            result->mlocation = copyLocation(Field(cilStmtkind, 3));
            result->sink = cilStmtkind;
            return result;
        }
        case Loop: {
            cilast::Loop* result = new cilast::Loop();
            result->mblock = copyBlock(Field(cilStmtkind, 0));
            result->mlocation = copyLocation(Field(cilStmtkind, 1));
            
            if(is_None(Field(cilStmtkind, 2)))
                result->mcontinueStmt = NULL;
            else
                result->mcontinueStmt = copyStmt(Some_val(Field(cilStmtkind, 2)));
            
            if(is_None(Field(cilStmtkind, 3)))
                result->mbreakStmt = NULL;
            else
                result->mbreakStmt = copyStmt(Some_val(Field(cilStmtkind, 3)));
            result->sink = cilStmtkind;
            return result;
        }
        case Block: {
            cilast::Block* result = new cilast::Block();
            result->mblock = copyBlock(Field(cilStmtkind, 0));
            result->sink = cilStmtkind;
            return result;
        }
        case TryFinally: {
            cilast::TryFinally* result = new cilast::TryFinally();
            result->sink = cilStmtkind;
            return result; 
        }
        case TryExcept: {
            cilast::TryExcept* result = new cilast::TryExcept();
            result->sink = cilStmtkind;
            return result; 
        }
        default:
            std::cout<<"copyStmtkind()::exit()";
            exit(1);
    }

}


/*
 Function definitions.
 */


cilast::block* copyBlock(CilBlock cilBlock) {
    CAMLparam1(cilBlock);

    cilast::block* result = new cilast::block();
    //result->battrs
    result->bstmts = copyArray(Field(cilBlock, 1), &copyStmt);
    result->sink = cilBlock;
    return result;
}

cilast::fundec* copyFundec(CilFundec cilFundec) {
    CAMLparam1(cilFundec);
    
    cilast::fundec* result;
    if(symbolTable.find(get_val(cilFundec))!=symbolTable.end()) {
        result = (cilast::fundec*)symbolTable[get_val(cilFundec)];
        //result->svar = copyVarinfo(get_svar(cilFundec));
    }
    else {
        result = new cilast::fundec();    
        symbolTable[get_val(cilFundec)] = result;
    
        result->svar = copyVarinfo(get_svar(cilFundec));
        result->sformals = copyArray(get_sformals(cilFundec), &copyVarinfo);
        result->slocals = copyArray(get_slocals(cilFundec), &copyVarinfo);
        result->smaxid = Int_val(get_smaxid(cilFundec));
        result->sbody = copyBlock(get_sbody(cilFundec));
        if(is_None(get_smaxstmtid(cilFundec)))
            result->smaxstmtid = -1;
        else
            result->smaxstmtid = Int_val(Some_val(get_smaxstmtid(cilFundec)));
        result->sallstmts = copyArray(get_sallstmts(cilFundec), &copyStmt);
        result->sink = cilFundec;
    }
    return result;
    
}

/*
 Initializers
 */
cilast::OffsetInit* copyOffsetInit(value v) {
    CAMLparam1(v);

    cilast::OffsetInit* result = new cilast::OffsetInit();
    result->moffset = copyOffset(Field(v, 0));
    result->minit = copyInit(Field(v, 1));
    result->sink = v;
    return result;
}

cilast::init* copyInit(CilInit cilInit) {
    CAMLparam1(cilInit);

    switch(Tag_val(cilInit)) {
        case SingleInit: {
            cilast::SingleInit* result = new cilast::SingleInit();
            result->mexp = copyExp(Field(cilInit, 0));
            result->sink = cilInit;
            return result;
        }
        case CompoundInit: {
            cilast::CompoundInit* result = new cilast::CompoundInit();
            result->mtyp = copyTyp(Field(cilInit, 0));
            result->minits = copyArray(Field(cilInit, 1), &copyOffsetInit);
            result->sink = cilInit;
            return result;
        }
        default:
            std::cout<<"copyInit()::exit()";
            exit(1);
    }


}


cilast::initinfo* copyInitinfo(CilInitinfo cilInitinfo) {
    CAMLparam1(cilInitinfo);

    cilast::initinfo* result = new cilast::initinfo();
    
    if(is_None(Field(cilInitinfo, 0)))
        result->minit = NULL;
    else
        result->minit = copyInit(Some_val(Field(cilInitinfo, 0)));
    result->sink = cilInitinfo;
    return result;
}


/*
 Lvalues
 */
cilast::offset* copyOffset(CilOffset cilOffset) {
    CAMLparam1(cilOffset);
    

    if(Is_block(cilOffset)) {
        switch(Tag_val(cilOffset)) {
            case Field: {
                cilast::OField* result = new cilast::OField();
                result->mfieldinfo = copyFieldinfo(Field(cilOffset, 0));
                result->moffset = copyOffset(Field(cilOffset, 1));
                result->sink = cilOffset;
                return result;
            }
            case Index: {
                cilast::Index* result = new cilast::Index();
                result->mexp = copyExp(Field(cilOffset, 0));
                result->moffset = copyOffset(Field(cilOffset, 1));
                result->sink = cilOffset;
                return result;
            }
            default:
                std::cout<<"copyOffset()::exit()";
                exit(1);
        }
    }
    else {
        switch(Int_val(cilOffset)) {
            case NoOffset: {
                cilast::NoOffset* result = new cilast::NoOffset();
                result->sink = cilOffset;
                return result;
            }
            default:
                std::cout<<"copyOffset()::exit()";
                exit(1);
        }
    }

    
}

cilast::lhost* copyLhost(CilLhost cilLhost) {
    CAMLparam1(cilLhost);

    
    if(symbolTable.find(get_val(cilLhost))!=symbolTable.end())
        return (cilast::lhost*)symbolTable[get_val(cilLhost)];                  

    switch(Tag_val(cilLhost)) {
        case Var: {
            cilast::Var* result = new cilast::Var();
            symbolTable[get_val(cilLhost)] = result;
            
            result->mvarinfo = copyVarinfo(Field(cilLhost, 0));
            result->sink = cilLhost;
            return result;
        }
        case Mem: {
            cilast::Mem* result = new cilast::Mem();
            symbolTable[get_val(cilLhost)] = result;
            
            result->mexp = copyExp(Field(cilLhost, 0));
            result->sink = cilLhost;
            return result;            
        }
        default:
            std::cout<<"copyLhost()::exit()";
            exit(1);
    }
    
}

cilast::lval* copyLval(CilLval cilLval) {
    CAMLparam1(cilLval);
    
    if(symbolTable.find(get_val(cilLval))!=symbolTable.end())
        return (cilast::lval*)symbolTable[get_val(cilLval)];  
    
    cilast::lval* result = new cilast::lval();
    symbolTable[get_val(cilLval)] = result;
    
    result->mlhost = copyLhost(get_lhost(cilLval));
    result->moffset = copyOffset(get_offset(cilLval));
    result->sink = cilLval;
    
    return result;
}



/*
 Constants
 */

cilast::constant* copyConstant(CilConstant cilConstant) {
    CAMLparam1(cilConstant);
    CAMLlocal1(list);
    
    switch(Tag_val(cilConstant)) {
        case CInt64:{
            cilast::CInt64* result = new cilast::CInt64();
            result->mint64 = Int64_val(Field(cilConstant, 0));         
            result->mikind = copyIkind(Field(cilConstant, 1));
            if(is_None(Field(cilConstant, 2)))
                result->mstr = NULL;
            else
                result->mstr = Copy_string(Some_val(Field(cilConstant, 2)));
            result->sink = cilConstant;
            return result;
        }
        case CStr: {
            cilast::CStr* result = new cilast::CStr();
            result->mstr = Copy_string(Field(cilConstant, 0));
            result->sink = cilConstant;
            return result;  
        }
        case CWStr: {
            cilast::CWStr* result = new cilast::CWStr();
            list = Field(cilConstant, 0);            
            for(; list!=NIL; list = LIST_NEXT(list)) {
                result->mint64s.push_back(Int64_val(LIST_DATA(list)));
            }
            result->sink = cilConstant;
            return result;

        }
        case CChr: {
            cilast::CChr* result = new cilast::CChr();
            result->mchar = Int_val(Field(cilConstant, 0));
            result->sink = cilConstant;
            return result;
        }
        case CReal: {
            cilast::CReal* result = new cilast::CReal();
            result->mfloat = Double_val(Field(cilConstant, 0));
            result->mfkind = copyFkind(Field(cilConstant, 1));
            if(is_None(Field(cilConstant, 2)))
                result->mstr = NULL;
            else
                result->mstr = Copy_string(Some_val(Field(cilConstant, 2)));
            result->sink = cilConstant;
            return result;
        }
        case CEnum: {
            cilast::CEnum* result = new cilast::CEnum();
            result->mexp = copyExp(Field(cilConstant, 0));
            result->mstr = Copy_string(Field(cilConstant, 1));
            result->menuminfo = copyEnuminfo(Field(cilConstant, 2));
            result->sink = cilConstant;
            return result;

        }
        default:
            std::cout<<"copyConstant()::exit()";
            exit(1);
    }

}

/*
 Expressions
 */

cilast::binop copyBinop(CilBinop cilBinop) {
    CAMLparam1(cilBinop);
    
    switch(Int_val(cilBinop)) {
        case PlusA:
            return cilast::PlusA;
        case PlusPI:
            return cilast::PlusPI;
        case IndexPI:
            return cilast::IndexPI;
        case MinusA:
            return cilast::MinusA;
        case MinusPI:
            return cilast::MinusPI;
        case MinusPP:
            return cilast::MinusPP;
        case Mult:
            return cilast::Mult;
        case Div:
            return cilast::Div;
        case Mod:
            return cilast::Mod;
        case Shiftlt:
            return cilast::Shiftlt;
        case Shiftrt:
            return cilast::Shiftrt;
        case Lt:
            return cilast::Lt;
        case Gt:
            return cilast::Gt;
        case Le:
            return cilast::Le;
        case Ge:
            return cilast::Ge;
        case Eq:
            return cilast::Eq;
        case Ne:
            return cilast::Ne;
        case BAnd:
            return cilast::BAnd;
        case BXor:
            return cilast::BXor;
        case BOr:
            return cilast::BOr;
        case LAnd:
            return cilast::LAnd;
        case LOr:
            return cilast::LOr;
        default:
            std::cout<<"copyBinop()::exit()";
            exit(1);
    }
    
}

cilast::unop copyUnop(CilUnop cilUnop) {
    CAMLparam1(cilUnop);
    
    switch(Int_val(cilUnop)) {
        case Neg:
            return cilast::Neg;
        case BNot:
            return cilast::BNot;
        case LNot:
            return cilast::LNot;
        default:
            exit(1);
    }
}

cilast::exp* copyExp(CilExp cilExp) {
    CAMLparam1(cilExp);


    switch(Tag_val(cilExp)) {
        case Const: {
            cilast::Const* result = new cilast::Const();
            result->mconstant = copyConstant(Field(cilExp, 0));
            result->sink = cilExp;
            return result;
        }
        case Lval: {
            cilast::Lval* result = new cilast::Lval();
            result->mlval = copyLval(Field(cilExp, 0));
            result->sink = cilExp;
            return result;
        }
        case SizeOf: {
            cilast::SizeOf* result = new cilast::SizeOf();
            result->mtyp = copyTyp(Field(cilExp, 0));
            result->sink = cilExp;
            return result;
        }
        case SizeOfE: {
            cilast::SizeOfE* result = new cilast::SizeOfE();
            result->mexp = copyExp(Field(cilExp, 0));
            result->sink = cilExp;
            return result;
        }
        case SizeOfStr: {
            cilast::SizeOfStr* result = new cilast::SizeOfStr();
            result->mstr = Copy_string(Field(cilExp, 0));
            result->sink = cilExp;
            return result;
        }
        case AlignOf: {
            cilast::AlignOf* result = new cilast::AlignOf();
            result->mtyp = copyTyp(Field(cilExp, 0));
            result->sink = cilExp;
            return result;
        }
        case AlignOfE: {
            cilast::AlignOfE* result = new cilast::AlignOfE();
            result->mexp = copyExp(Field(cilExp, 0));
            result->sink = cilExp;
            return result;
        }
        case UnOp: {
            cilast::UnOp* result = new cilast::UnOp();
            result->munop = copyUnop(Field(cilExp, 0));
            result->mop1 = copyExp(Field(cilExp, 1));
            result->mtyp = copyTyp(Field(cilExp, 2));
            result->sink = cilExp;
            return result;
        }
        case BinOp: {
            cilast::BinOp* result = new cilast::BinOp();
            result->mbinop = copyBinop(Field(cilExp, 0));
            result->mop1 = copyExp(Field(cilExp, 1));
            result->mop2 = copyExp(Field(cilExp, 2));
            result->mtyp = copyTyp(Field(cilExp, 3));
            result->sink = cilExp;
            return result;
        }
        case CastE: {
            cilast::CastE* result = new cilast::CastE();
            result->mtyp = copyTyp(Field(cilExp, 0));
            result->mexp = copyExp(Field(cilExp, 1));
            result->sink = cilExp;
            return result;
        }
        case AddrOf: {
            cilast::AddrOf* result = new cilast::AddrOf();
            result->mlval = copyLval(Field(cilExp, 0));
            result->sink = cilExp;
            return result;
        }
        case StartOf: {
            cilast::AddrOf* result = new cilast::AddrOf();
            result->mlval = copyLval(Field(cilExp, 0));
            result->sink = cilExp;
            return result;
        }
        default:
            std::cout<<"copyExp()::exit()";
            exit(1);
    }
        

}




/*
 Variables
 */
cilast::varinfo* copyVarinfo(CilVarinfo cilVarinfo) {
    CAMLparam1(cilVarinfo);
    
    cilast::varinfo* result;
    if(symbolTable.find(get_val(cilVarinfo))!=symbolTable.end()) {
        result = (cilast::varinfo*)symbolTable[get_val(cilVarinfo)];  
    }
    else {
        result = new cilast::varinfo();   
        symbolTable[get_val(cilVarinfo)] = result;

        result->vname = Copy_string(get_vname(cilVarinfo));
        result->vtype = copyTyp(get_vtype(cilVarinfo));
        //get_vattr
        //get_vstorage
        result->vglob = Bool_val(get_vglob(cilVarinfo));
        result->vinline = Bool_val(get_vinline(cilVarinfo));
        result->vdecl = copyLocation(get_vdecl(cilVarinfo));
        result->vid = Int_val(get_vid(cilVarinfo));
        result->vaddrof = Bool_val(get_vaddrof(cilVarinfo));
        result->vreferenced = Bool_val(get_vreferenced(cilVarinfo));
        //get_vdescr
        result->vdescrpure = Bool_val(get_vdescrpure(cilVarinfo));
        result->sink = cilVarinfo;
    }

    return result;
    
}

cilast::storage copyStorage(CilStorage cilStorage) {
    CAMLparam1(cilStorage);
    
    switch(Int_val(cilStorage)){
        case NoStorage:
            return cilast::NoStorage;
        case Static:
            return cilast::Static;
        case Register:
            return cilast::Register;
        case Extern:
            return cilast::Extern;
        default:
            std::cout<<"copyStorage()::exit()";
            exit(1);
    }
    
    
}

/*
 Enumerations
 */
cilast::enumitem* copyEnumitem(value v) {
    CAMLparam1(v);
    
    cilast::enumitem* result = new cilast::enumitem();
    result->name = Copy_string(Field(v, 0));
    result->value = copyExp(Field(v, 1));
    result->loc = copyLocation(Field(v, 2));
    return result;
    
}    
    
cilast::enuminfo* copyEnuminfo(CilEnuminfo cilEnuminfo) {
    CAMLparam1(cilEnuminfo);
        
    if(symbolTable.find(get_val(cilEnuminfo))!=symbolTable.end())
        return (cilast::enuminfo*)symbolTable[get_val(cilEnuminfo)];  
    
    cilast::enuminfo* result = new cilast::enuminfo();
    symbolTable[get_val(cilEnuminfo)] = result;
    
    result->ename = Copy_string(get_ename(cilEnuminfo));
    result->eitems = copyArray(get_eitems(cilEnuminfo), &copyEnumitem);
    result->eattr = copyArray(get_eattr(cilEnuminfo), &copyAttribute);
    result->ereferenced = Bool_val(get_ereferenced(cilEnuminfo));
    result->ekind = copyIkind(get_ekind(cilEnuminfo));    
    result->sink = cilEnuminfo;
    return result;
}

cilast::typeinfo* copyTypeinfo(CilTypeinfo cilTypeinfo) {
    CAMLparam1(cilTypeinfo);
    
    if(symbolTable.find(get_val(cilTypeinfo))!=symbolTable.end())
        return (cilast::typeinfo*)symbolTable[get_val(cilTypeinfo)];  
    
    cilast::typeinfo* result = new cilast::typeinfo();    
    symbolTable[get_val(cilTypeinfo)] = result;
    
    result->tname = Copy_string(get_tname(cilTypeinfo));
    result->ttype = copyTyp(get_ttype(cilTypeinfo));
    result->treferenced = Bool_val(get_treferenced(cilTypeinfo));
    result->sink = cilTypeinfo;
    return result;
}


/*
 Structure fields
 */

cilast::fieldinfo* copyFieldinfo(CilFieldinfo cilFieldinfo) {
    CAMLparam1(cilFieldinfo);
    
    if(symbolTable.find(get_val(cilFieldinfo))!=symbolTable.end())
        return (cilast::fieldinfo*)symbolTable[get_val(cilFieldinfo)];      
    
    cilast::fieldinfo* result = new cilast::fieldinfo();
    symbolTable[get_val(cilFieldinfo)] = result;
    result->fname = Copy_string(get_fname(cilFieldinfo));
    result->fcomp = copyCompinfo(get_fcomp(cilFieldinfo));
    result->ftype = copyTyp(get_ftype(cilFieldinfo));
    if(is_None(get_fbitfield(cilFieldinfo)))
       result->fbitfield = -1;
    else
        result->fbitfield = Int_val(Some_val(get_fbitfield(cilFieldinfo)));
    
    result->fattr = copyArray(get_fattr(cilFieldinfo), &copyAttribute);
    result->floc = copyLocation(get_floc(cilFieldinfo));
    result->sink = cilFieldinfo;
    return result;
}


/*
 Structures
 */
cilast::compinfo* copyCompinfo(CilCompinfo cilCompinfo) {
    CAMLparam1(cilCompinfo);
    
    
    if(symbolTable.find(get_val(cilCompinfo))!=symbolTable.end())
        return (cilast::compinfo*)symbolTable[get_val(cilCompinfo)];  
    
    cilast::compinfo* result = new cilast::compinfo();    
    symbolTable[get_val(cilCompinfo)] = result;
    
    result->cstruct = Bool_val(get_cstruct(cilCompinfo));
    result->cname = Copy_string(get_cname(cilCompinfo));
    result->ckey = Int_val(get_ckey(cilCompinfo));
    result->cfields = copyArray(get_cfields(cilCompinfo), &copyFieldinfo);
    result->cattr = copyArray(get_cattr(cilCompinfo), &copyAttribute);
    result->cdefined = Bool_val(get_cdefined(cilCompinfo));
    result->creferenced = Bool_val(get_creferenced(cilCompinfo));    
    result->sink = cilCompinfo;
    return result;
}

/*
 Attributes
 */

cilast::attribute* copyAttribute(CilAttribute cilAttribute) {
    CAMLparam1(cilAttribute);
    
    cilast::attribute* result = new cilast::attribute();
    
    result->name = Copy_string(Field(cilAttribute, 0));
    result->params = copyArray(Field(cilAttribute, 1), &copyAttrparam);
    result->sink = cilAttribute;
    return result;
}

cilast::attrparam* copyAttrparam(CilAttrparam cilAttrparam) {
    CAMLparam1(cilAttrparam);
    cilast::attrparam* result;
    
    switch(Tag_val(cilAttrparam)) {
        case AInt: {
            cilast::AInt* tmp = new cilast::AInt();
            tmp->mi = Int_val(Field(cilAttrparam, 0));
            result->sink = cilAttrparam;
            result = tmp;
        }
        case AStr: {
            cilast::AStr* result = new cilast::AStr();
            result->mstr = Copy_string(Field(cilAttrparam, 0));
            result->sink = cilAttrparam;
            return result;
        }
        case ACons: {
            cilast::ACons* result = new cilast::ACons();    
            result->mstr = Copy_string(Field(cilAttrparam, 0));
            result->mattrparams = copyArray(Field(cilAttrparam, 1), &copyAttrparam);
            result->sink = cilAttrparam;
            return result;
        }
        case ASizeOf: {
            cilast::ASizeOf* result = new cilast::ASizeOf();
            result->mtyp = copyTyp(Field(cilAttrparam, 0));
            result->sink = cilAttrparam;
            return result;
        }
        case ASizeOfE: {
            cilast::ASizeOfE* result = new cilast::ASizeOfE();
            result->mattrparam = copyAttrparam(Field(cilAttrparam, 0));
            result->sink = cilAttrparam;
            return result;
        }
        case ASizeOfS: {
            cilast::ASizeOfS* result = new cilast::ASizeOfS();
            result->mtypsig = copyTypsig(Field(cilAttrparam, 0));
            result->sink = cilAttrparam;
            return result;
        }
        case AAlignOf: {
            cilast::AAlignOf* result = new cilast::AAlignOf();
            result->mtyp = copyTyp(Field(cilAttrparam, 0));
            result->sink = cilAttrparam;
            return result;
        }
        case AAlignOfE: {
            cilast::AAlignOfE* result = new cilast::AAlignOfE();
            result->mattrparam = copyAttrparam(Field(cilAttrparam, 0));
            result->sink = cilAttrparam;
            return result;
        }
        case AAlignOfS: {
            cilast::AAlignOfS* result = new cilast::AAlignOfS();
            result->mtypsig = copyTypsig(Field(cilAttrparam, 0));
            result->sink = cilAttrparam;
            return result;
        }            
        case AUnOp: {
            cilast::AUnOp* result = new cilast::AUnOp();
            result->munop = copyUnop(Field(cilAttrparam, 0));
            result->mattrparam = copyAttrparam(Field(cilAttrparam, 1));
            result->sink = cilAttrparam;
            return result;
        }
        case ABinOp: {
            cilast::ABinOp* result = new cilast::ABinOp();
            result->mbinop = copyBinop(Field(cilAttrparam, 0));
            result->mattrparam = copyAttrparam(Field(cilAttrparam, 1));
            result->sink = cilAttrparam;
            return result;
        }
        case ADot: {
            cilast::ADot* result = new cilast::ADot();
            result->mattrparam = copyAttrparam(Field(cilAttrparam, 0));
            result->mstr = Copy_string(Field(cilAttrparam, 1));
            result->sink = cilAttrparam;
            return result;
        }
        case AStar: {
            cilast::AStar* result = new cilast::AStar();
            result->mattrparam = copyAttrparam(Field(cilAttrparam, 0));
            result->sink = cilAttrparam;
            return result;
        }
        case AAddrOf: {
            cilast::AAddrOf* result = new cilast::AAddrOf();
            result->mattrparam = copyAttrparam(Field(cilAttrparam, 0));
            result->sink = cilAttrparam;
            return result;
        }
        case AIndex: {
            cilast::AIndex* result = new cilast::AIndex();
            result->mattrparam1 = copyAttrparam(Field(cilAttrparam, 0));
            result->mattrparam2 = copyAttrparam(Field(cilAttrparam, 1));
            result->sink = cilAttrparam;
            return result;
        }
        case AQuestion:
            std::cout<<"copyAttrparam()::exit()";
            exit(1);
    }

    
}

/*
 Types
 */
cilast::ikind copyIkind(CilIkind cilIkind) {
    CAMLparam1(cilIkind);

    switch(Int_val(cilIkind)) {
        case IChar:
            return cilast::IChar;
        case ISChar:
            return cilast::ISChar;
        case IUChar:
            return cilast::IUChar;
        case IBool:
            return cilast::IBool;
        case IInt:
            return cilast::IInt;
        case IUInt:
            return cilast::IUInt;
        case IShort:
            return cilast::IShort;
        case IUShort:
            return cilast::IUShort;
        case ILong:
            return cilast::ILong;
        case IULong:
            return cilast::IULong;
        case ILongLong:
            return cilast::ILongLong;
        case IULongLong:
            return cilast::IULongLong;            
        default:
            std::cout<<"copyIkind()::exit()";
            //exit(1);
    }
    
}


cilast::fkind copyFkind(CilFkind cilFkind) {
    CAMLparam1(cilFkind);
    
    switch(Int_val(cilFkind)) {
        case FFloat:
            return cilast::FFloat;            
        case FDouble:
            return cilast::FDouble;
        case FLongDouble:
            return cilast::FLongDouble;
        default:
            std::cout<<"copyFkind()::exit()";
            exit(1);
    }
    
}



cilast::arg* copyArg(value v) {
    CAMLparam1(v);
    
    cilast::arg* result = new cilast::arg();
    result->mname = Copy_string(Field(v, 0));
    result->mtyp = copyTyp(Field(v, 1));
    result->mattributes = copyArray(Field(v, 2), &copyAttribute);
    
    return result;
    
}

cilast::typ* copyTyp(CilTyp cilTyp) {
    CAMLparam1(cilTyp);
         
    switch(Tag_val(cilTyp)) {
        case TVoid: {
            cilast::TVoid* result = new cilast::TVoid();
            result->sink = cilTyp;
            return result;
        }
        case TInt: {
            cilast::TInt* result = new cilast::TInt();
            result->mikind = copyIkind(Field(cilTyp, 0));
            result->sink = cilTyp;
            return result;
        }
        case TFloat: {
            cilast::TFloat* result = new cilast::TFloat();
            result->mfkind = copyFkind(Field(cilTyp, 0));
            result->sink = cilTyp;
            return result;
        }
        case TPtr: {
            cilast::TPtr* result = new cilast::TPtr(); 
            result->mtyp = copyTyp(Field(cilTyp, 0));
            result->sink = cilTyp;
            return result;
        }
        case TArray: {
            cilast::TArray* result = new cilast::TArray();
            result->mtyp = copyTyp(Field(cilTyp, 0));
            if(is_None(Field(cilTyp, 1)))
                result->mexp = NULL;
            else
                result->mexp = copyExp(Some_val(Field(cilTyp, 1)));
            result->sink = cilTyp;
            return result;
        }
        case TFun: {
            cilast::TFun* result = new cilast::TFun();
            result->mtyp = copyTyp(Field(cilTyp, 0));            
            if(is_None(Field(cilTyp, 1)))
                ;
            else                    
                result->margs = copyArray(Some_val(Field(cilTyp, 1)), &copyArg);
            result->mbool = Bool_val(Field(cilTyp, 2));
            result->mattributes = copyArray(Field(cilTyp, 3), &copyAttribute);
            result->sink = cilTyp;
            return result;
        }
        case TNamed: {
            cilast::TNamed* result = new cilast::TNamed();
            result->mtypeinfo = copyTypeinfo(Field(cilTyp, 0));
            result->mattributes = copyArray(Field(cilTyp, 1), &copyAttribute);
            result->sink = cilTyp;
            return result;            
        }
        case TComp: {
            cilast::TComp* result = new cilast::TComp();
            result->mcompinfo = copyCompinfo(Field(cilTyp, 0));
            result->mattributes = copyArray(Field(cilTyp, 1), &copyAttribute);
            result->sink = cilTyp;
            return result;            
        }
        case TEnum: {
            cilast::TEnum* result = new cilast::TEnum();
            result->menuminfo = copyEnuminfo(Field(cilTyp, 0));
            result->mattributes = copyArray(Field(cilTyp, 1), &copyAttribute);
            result->sink = cilTyp;
            return result;            
        }
        case TBuiltin_va_list: {
            cilast::TBuiltin_va_list* result = new cilast::TBuiltin_va_list();
            result->mattributes = copyArray(Field(cilTyp, 0), &copyAttribute);
            result->sink = cilTyp;
            return result;            
        }
        default:
            std::cout<<"copyTyp()::exit()";
            exit(1);
    }

}

/*
 Globals
 */
cilast::global* copyGlobal(CilGlobal cilGlobal) {
    CAMLparam1(cilGlobal);
    
    switch(Tag_val(cilGlobal)) {
        case GType: {
            cilast::GType* result = new cilast::GType();
            
            result->mtypeinfo = copyTypeinfo(Field(cilGlobal, 0));
            result->mlocation = copyLocation(Field(cilGlobal, 1));
            result->sink = cilGlobal;
            return result;
        }
        case GCompTag: {
            cilast::GCompTag* result = new cilast::GCompTag();
            
            result->mcompinfo = copyCompinfo(Field(cilGlobal, 0));
            result->mlocation = copyLocation(Field(cilGlobal, 1));
            result->sink = cilGlobal;
            return result;
        }      
        case GCompTagDecl: {
            cilast::GCompTagDecl* result = new cilast::GCompTagDecl();
            
            result->mcompinfo = copyCompinfo(Field(cilGlobal, 0));
            result->mlocation = copyLocation(Field(cilGlobal, 1));
            result->sink = cilGlobal;
            return result;
        }
        case GEnumTag: {
            cilast::GEnumTag* result = new cilast::GEnumTag();
            
            result->menuminfo = copyEnuminfo(Field(cilGlobal, 0));
            result->mlocation = copyLocation(Field(cilGlobal, 1));
            result->sink = cilGlobal;
            return result;
        }
        case GEnumTagDecl: {
            cilast::GEnumTagDecl* result = new cilast::GEnumTagDecl();
            
            result->menuminfo = copyEnuminfo(Field(cilGlobal, 0));
            result->mlocation = copyLocation(Field(cilGlobal, 1));
            result->sink = cilGlobal;
            return result;
        }
        case GVar:{
            cilast::GVar* result = new cilast::GVar();
            
            result->mvarinfo = copyVarinfo(Field(cilGlobal, 0));
            result->minitinfo = copyInitinfo(Field(cilGlobal, 1));
            result->mlocation = copyLocation(Field(cilGlobal, 2));
            result->sink = cilGlobal;
            
            return result;
        }
        case GVarDecl: {
            cilast::GVarDecl* result = new cilast::GVarDecl();
            
            result->mvarinfo = copyVarinfo(Field(cilGlobal, 0));
            result->mlocation = copyLocation(Field(cilGlobal, 1));
            result->sink = cilGlobal;
            return result;
        }
        case GFun: {
            cilast::GFun* result = new cilast::GFun();
            result->mfundec = copyFundec(Field(cilGlobal, 0));
            result->mlocation = copyLocation(Field(cilGlobal, 1)); 
            
            if(strcmp(result->mfundec->svar->vname,"main")==0)
                mainFundec = result->mfundec;
            result->sink = cilGlobal;
            return result;
        }
        case GAsm: {
            cilast::GAsm* result = new cilast::GAsm();
            result->sink = cilGlobal;
            return result;
        }
        case GPragma: {
            cilast::GPragma* result = new cilast::GPragma();
            result->sink = cilGlobal;
            return result;
        }
        case GText: {
            cilast::GText* result = new cilast::GText();
            result->sink = cilGlobal;
            return result;
        }
        default:          
            std::cout<<"copyGlobal()::exit()";
            //exit(1);
    }
}

/*
 File
 */

cilast::file* copyFile(CilFile cilFile) {
    CAMLparam1(cilFile);

    cilast::file* result = new cilast::file();
    result->fileName = Copy_string(get_fileName(cilFile));
    result->globals = copyArray(get_globals(cilFile), &copyGlobal);
    
    if(is_None(get_globinit(cilFile)))
        result->globinit = NULL;
    else
        result->globinit = copyFundec(Some_val(get_globinit(cilFile)));
    
    result->mainFundec = mainFundec;
    result->sink = cilFile;
    return result;
}


cilast::file* parse(std::string filePath) {
    char* argv[] = {};
    caml_startup(argv);
    
    CAMLlocal4(cilFile, list, data, fdec);
    
    //prepare cil
    cilFile = Frontc_parse_wrap(filePath.c_str());    
    for(list = get_globals(cilFile); list!=NIL; list = LIST_NEXT(list)) {  	
        data = LIST_DATA(list);
        
        switch(Tag_val(data)) {
            case GFun:       
                fdec = Field(data, 0);
                Cil_prepareCFG(fdec);
                Cil_computeCFGInfo(fdec, Val_bool(0));
                break;
            default:                
                break;
        }
    }
    
    //prepare ast
    cilast::file* ret = copyFile(cilFile);
    computeFileIPDom(ret);


    return ret;
    
}

